import collections
import json
import random

from loguru import logger

# 明确导入配置模块中的具体内容
from FindWay.configs.config import *


class RoadPathFinder:
    """
    路径查找器类，用于在地图上查找最短路径。
    """
    def __init__(self, road_model, rows, cols):
        self.road_model = road_model  # 地图模型
        self.rows = rows  # 地图行数
        self.cols = cols  # 地图列数
        self.directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 四个方向的偏移量

    def find_shortest_path(self, start_ip, end_ip):
        """
        使用广度优先搜索查找最短路径。
        :param start_ip: 起点坐标字典
        :param end_ip: 终点坐标字典
        :return: 最短路径列表，如果找不到路径则返回 None
        """
        if start_ip == end_ip:
            return [start_ip]

        visited = [[False] * self.cols for _ in range(self.rows)]
        queue = collections.deque([(start_ip, [])])
        visited[start_ip['row']][start_ip['col']] = True

        while queue:
            current_ip, path = queue.popleft()
            if current_ip == end_ip:
                return path + [current_ip]

            for dr, dc in self.directions:
                new_row = current_ip['row'] + dr
                new_col = current_ip['col'] + dc
                if 0 <= new_row < self.rows and 0 <= new_col < self.cols and not visited[new_row][new_col] and self.road_model[new_row][new_col] == 1:
                    visited[new_row][new_col] = True
                    queue.append(({'row': new_row, 'col': new_col}, path + [current_ip]))

        return None

class RoadModelGenerator:
    """
    地图生成器类，用于生成随机地图和设置起点、终点。
    """

    def __init__(self, rows=10, cols=10):
        self.road_model = None  # 生成的地图模型
        self.start_ip = None  # 起点坐标
        self.end_ip = None  # 终点坐标
        self.rows = rows  # 地图行数
        self.cols = cols  # 地图列数
        self._generate_random_seed()

    def _generate_random_seed(self):
        self.seed = random.randint(0, 99999999)
        random.seed(self.seed)

    def generate_road_model(self):
        # 保证一定比例的可通行区域
        min_accessible_cells = int(self.rows * self.cols * min_accessible_ratio)
        accessible_cells = set()
        if min_accessible_ratio == 0:
            raise ValueError("Invalid min_accessible_ratio: 0")
        while len(accessible_cells) < min_accessible_cells:
            row = random.randint(0, self.rows - 1)
            col = random.randint(0, self.cols - 1)
            accessible_cells.add((row, col))
        self.road_model = [[0] * self.cols for _ in range(self.rows)]
        for row, col in accessible_cells:
            self.road_model[row][col] = 1

        # 如果是单行地图，确保至少有两个可通行位置
        if self.rows == 1:
            while sum(self.road_model[0]) < 2:
                new_col = random.randint(0, self.cols - 1)
                if self.road_model[0][new_col] == 0:
                    self.road_model[0][new_col] = 1

    def set_random_start_end(self):
        """
        设置随机的起点和终点。
        """
        if self.rows == 1 and self.cols == 1:
            self.start_ip = {'row': 0, 'col': 0}
            self.end_ip = {'row': 0, 'col': 0}
            return

        # 如果只有一行，手动设置起点和终点
        if self.rows == 1:
            valid_positions = [{'row': 0, 'col': col} for col in range(self.cols) if self.road_model[0][col] == 1]
            if not valid_positions:
                raise ValueError("No valid positions for start and end in single row map.")
            self.start_ip = valid_positions[0]
            self.end_ip = valid_positions[1] if len(valid_positions) > 1 else valid_positions[0]
            while self.end_ip == self.start_ip:
                self.end_ip = valid_positions[random.randint(0, len(valid_positions)-1)]
            return

        def find_valid_position_not_wall():
            # 确定可能的起点和终点区域
            valid_positions = [(row, col) for row in range(self.rows) for col in range(self.cols) if self.road_model[row][col] == 1]
            if not valid_positions:
                return None
            return {'row': random.choice(valid_positions)[0], 'col': random.choice(valid_positions)[1]}

        self.start_ip = find_valid_position_not_wall()
        self.end_ip = find_valid_position_not_wall()
        while self.end_ip == self.start_ip:
            self.end_ip = find_valid_position_not_wall()

    def get_seed(self):
        """
        返回生成的种子值。
        """
        return self.seed


# 优化算法
import heapq

class AStarPathFinder:
    """
    A*算法路径查找器类。
    """
    def __init__(self, road_model, rows, cols):
        self.road_model = road_model  # 地图模型
        self.rows = rows  # 地图行数
        self.cols = cols  # 地图列数
        self.directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 四个方向的偏移量

    def heuristic(self, a, b):
        """
        启发式函数，计算两个点的曼哈顿距离。
        """
        a_tuple = (a['row'], a['col'])
        b_tuple = (b['row'], b['col'])
        return abs(a_tuple[0] - b_tuple[0]) + abs(a_tuple[1] - b_tuple[1])

    def find_shortest_path(self, start_ip, end_ip):
        """
        使用 A*算法查找最短路径。
        :param start_ip: 起点坐标字典
        :param end_ip: 终点坐标字典
        :return: 最短路径列表，如果找不到路径则返回 None
        """
        if start_ip == end_ip:
            return [start_ip]

        start_tuple = (start_ip['row'], start_ip['col'])
        end_tuple = (end_ip['row'], end_ip['col'])

        open_set = [(0, start_tuple)]
        came_from = {}
        g_score = {start_tuple: 0}
        f_score = {start_tuple: self.heuristic(start_ip, end_ip)}

        while open_set:
            current_f_score, current_ip_tuple = heapq.heappop(open_set)
            current_ip = {'row': current_ip_tuple[0], 'col': current_ip_tuple[1]}

            if current_ip == end_ip:
                path = [current_ip]
                while tuple(current_ip) in came_from:
                    current_ip = came_from[tuple(current_ip)]
                    path.append(current_ip)
                return path[::-1]

            for dr, dc in self.directions:
                new_row = current_ip['row'] + dr
                new_col = current_ip['col'] + dc
                if 0 <= new_row < self.rows and 0 <= new_col < self.cols and self.road_model[new_row][new_col] == 1:
                    tentative_g_score = g_score[current_ip_tuple] + 1
                    neighbor = {'row': new_row, 'col': new_col}
                    neighbor_tuple = (neighbor['row'], neighbor['col'])
                    if neighbor_tuple not in g_score or tentative_g_score < g_score[neighbor_tuple]:
                        came_from[neighbor_tuple] = current_ip
                        g_score[neighbor_tuple] = tentative_g_score
                        f_score[neighbor_tuple] = tentative_g_score + self.heuristic(neighbor, end_ip)
                        heapq.heappush(open_set, (f_score[neighbor_tuple], neighbor_tuple))

        return None

class Creation:
    """
    整体流程控制类。
    """
    def __init__(self):
        self.generator = None  # 地图生成器实例
        self.shortest_path = None  # 最短路径
        self.finder = None  # 路径查找器实例
        self.road_model = None  # 地图模型
        self.start_ip = None  # 起点坐标
        self.end_ip = None  # 终点坐标

    def build(self, rows=10, cols=10):
        """
        构建地图和路径。
        :param rows: 地图行数
        :param cols: 地图列数
        :return: 构建是否成功
        """
        logger.info("Building...")
        self.generator = RoadModelGenerator(rows, cols)
        try:
            logger.info("Building map...")
            self.generator.generate_road_model()
            logger.info("Building shortest path...")
            self.generator.set_random_start_end()
        except Exception as e:
            logger.error(f"Error during building: {e}")
            return False
        return True

    def verification(self):
        """
        验证路径是否存在。
        """
        logger.info("Verifying...")
        # 使用 AStarPathFinder 代替 RoadPathFinder
        self.finder = AStarPathFinder(self.generator.road_model, self.generator.rows, self.generator.cols)
        self.shortest_path = self.finder.find_shortest_path(self.generator.start_ip, self.generator.end_ip)

    def get_key(self, retry_count):
        data = f"{int(min_accessible_ratio*10)}-{rows}-{cols}-{retry_count}-{self.generator.get_seed()}"


    def out_map(self, name, road_model):
        """
        输出地图信息。
        :param name: 地图名称
        :param road_model: 地图模型
        """
        print(road_model)
        for i in range(len(road_model)):
            for j in range(len(road_model[i])):
                road_model[i][j] = f"({i},{j}) {road_model[i][j]}"
        logger.info(f"{name}:\n {road_model}")

    def out_ip(self, name, ip):
        """
        输出坐标信息。
        :param name: 坐标名称
        :param ip: 坐标字典
        """
        logger.info(f'{name}:')
        logger.info(f" {ip['row'], ip['col']}")

    def out_way(self, name, way):
        """
        输出路径信息。
        :param name: 路径名称
        :param way: 路径列表
        """
        logger.info(f'{name}:')
        k = ""
        for point in way:
            k += f" ({point['row']}, {point['col']})" + " ->" if way[-1]!= point else ""
        logger.info(k)


# 使用示例

def text():
    """
    输出地图、起点和终点信息。
    """
    # creation.out_map("map", creation.generator.road_model)
    creation.out_ip("start_ip", creation.generator.start_ip)
    creation.out_ip("end_ip", creation.generator.end_ip)

def _config():
    """
    配置日志记录。
    """
    if not debug:
        logger.add(log_filename, rotation=max_retries)
    logger.info("configs:")
    logger.info(f" time: {now.strftime('%Y-%m-%d %H:%M:%S')}")
    logger.info(f" BuildAlgorithm: RoadModelGenerator")
    logger.info(f" PathAlgorithm: AStarPathFinder")
    logger.info(f" rows: {rows}")
    logger.info(f" cols: {cols}")
    logger.info(f" max_retries: {max_retries}")
    logger.info(f" min_accessible_ratio: {min_accessible_ratio}")
    logger.info(f" debug: {debug}")
    logger.info(f" rotation: {rotation}")
    logger.info(f" log type: {log_type}")
    logger.info(f" seeds type: {seeds_type}")
    logger.info(f" log path: {log_filename}")
    logger.info(f" seeds path: {data_filename}")

creation = Creation()
_config()

def main():
    """
    主函数，执行地图生成和路径查找的多次重试。
    :return: 成功找到路径的次数
    """
    success_count = 0
    retry_count = 0
    seeds = {}
    while retry_count < max_retries:

        logger.info(f"***{retry_count + 1} ({(success_count / max_retries) * 100}%)***")
        if creation.build(rows, cols):
            id = f"{int(min_accessible_ratio*10)}-{rows}-{cols}-{retry_count}-{creation.generator.get_seed()}"
            creation.verification()
            seeds.update({retry_count:{"seed":creation.generator.get_seed(), "id": id, "type": 1 if creation.shortest_path else 0}})
            if creation.shortest_path:
                logger.info(f"Find way at id={id}")
                success_count += 1
                text()
                creation.out_way("way", creation.shortest_path)
            else:
                logger.error(f"Can not find way at id={id}")
                text()
        else:
            logger.error(f"Failed to build. Retrying...")
        retry_count += 1

    logger.info(f"ok {success_count}/{max_retries} {(success_count / max_retries) * 100}%")
    logger.info(f"seeds:")
    logger.info(f" {seeds}")
    with open(data_filename, "w+") as f:
        f.write(json.dumps(seeds))
    return success_count

if __name__ == "__main__":
    main()